//
//  Layer_Creatures.m
//  Return Of The Lemi
//
//  Created by Paul Vishayanuroj on 11/1/09.
//  Copyright 2009 Cornell University. All rights reserved.
//

#import "Layer_Creatures.h"

static int kumquatCollision(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data)
{
	Layer_Creatures *layer = (Layer_Creatures *)data;
	//[layer createExplosion:CGPointMake(200, 150)];
	[layer createExplosion:CGPointMake(b->body->p.x, b->body->p.y)];	
	// This is so goddamned cheap, but it works. I can't actually get rid of it here since it is still being processed by Chipmunk
	b->body->p = cpv(-800, -800);
	//[layer RemoveKumquat:b->data];
	// Return 0 to indicate no elastic collision
	return 0;
}

static int munitionsCollision(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data)
{
	return 0;
}

static int playerHit(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data)
{
	NSLog(@"player hit");
	return 0;
}

@implementation Layer_Creatures

@synthesize player, enemies, munitions;

// Initialize the layer instance
- (id) init 
{
	if((self = [super init])) 
	{
		isTouchEnabled = YES;
		// Tag yourself, since the parallax node constructor doesn't allow you to
		self.tag = kTagCreatures;
		
		// Setup physics system
		[self SetupSpace];
		
		// Setup munitions array
		munitions = [[NSMutableArray alloc] initWithCapacity:20];
		
		// Setup enemies array
		enemies = [[NSMutableArray alloc] initWithCapacity:24];
		
		// Add the player as a child of the creature layer
		player = [[Player playerWithPos:(CGPointMake(CONST_PLAYER_START_X, CONST_PLAYER_START_Y))] retain];
		// Adds player sprite manager to layer (increases retain count)
		[self addChild:player.spriteManager];
		
		cpSpaceAddBody(space, player.body);
		
		cpSpaceAddShape(space, player.shape);
		
		// TEST - add a meerkat
		meerkat = [[Meerkat meerkatWithPos:player position:(CGPointMake(CONST_PLAYER_START_X + 150, CONST_PLAYER_START_Y - 15))] retain];
		[self addChild:meerkat.spriteManager];
		[enemies addObject:meerkat];
		
		// Meerkat physics
		
		// Add the body to space
		cpSpaceAddBody(space, meerkat.body);		
		
		// Add the shape to our space
		cpSpaceAddShape(space, meerkat.shape);			
		
		// Add the meerkat and kumquat as a collision pair
		//cpSpaceAddCollisionPairFunc(space, cTagEnemy, cTagPlayerMunitions, &kumquatCollision, self);	
		
		//cpSpaceAddCollisionPairFunc(space, cTagEnemyMunitions, cTagPlayerMunitions, &munitionsCollision, self);
		
		//cpSpaceAddCollisionPairFunc(space, cTagEnemyMunitions, cTagPlayer, &playerHit, self);
		
		// Schedule update function to run every frame
		[self schedule:@selector(Update) interval: (1.0f/CONST_FRAME_RATE)];
		// Sehdule slower update to run every at 2 fps (0.5 s)
		[self schedule:@selector(SlowUpdate) interval: (1.0f/2.0f)]; 
	}
	return self;
}

- (void) SetupSpace
{
	// Start chipmunk
	cpInitChipmunk();
	
	// Create a space object
	space = cpSpaceNew();
	
	// Define a gravity vector
	space->gravity = cpv(0, -500);
	// Add elastic effects
	space->elasticIterations = 5;		
	
	// Define a static body to attach our floor to
	cpBody *staticBody = cpBodyNew(INFINITY, INFINITY);
	
	// Define the floor
	cpShape *floorShape = cpSegmentShapeNew(staticBody, cpv(-1000, 50), cpv(1000, 50), 0.0f);
	floorShape->e = 0.8f; // Elasticity
	floorShape->u = 1.0f; // Friction
	floorShape->collision_type = cTagFloor;
	cpSpaceAddStaticShape(space, floorShape);	
}

// Munitions handling
- (void) fireMunition:(MunitionType)type position:(CGPoint)startPos
{
	Munitions *munition;
	switch(type)
	{
		case kumquatYellow:
			munition = [Kumquat kumquatFromDrag:touchStart stop:touchEnd pos:startPos space:space];
			// Add to munitions array to keep track of
			[munitions addObject:munition];
			// Add it to the layer to be drawn
			[self addChild:munition.sprite z:1];						
			break;
		case kumquatRed:
			break;
		case kumquatBlue:
			break;
		case pebble:
			munition = [Pebble pebbleWithPosition:startPos space:space];
			// Add to munitions array to keep track of
			[munitions addObject:munition];
			// Add it to the layer to be drawn
			[self addChild:munition.sprite z:1];				
			break;
		case rock:
			break;
	}
}

///////////////////////////////////////
// Player touch detection /////////////
///////////////////////////////////////

// When the player first puts their finger down on the screen
- (BOOL) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
	// Check if the player touched within the same relative horizontal plane as the character
	// If so, check the direction and walk in that direction
	UITouch *touch = [touches anyObject];
	CGPoint point = [touch locationInView: [touch view]];
	touchStart = [[Director sharedDirector] convertToGL:point];
	
	// Detect double touches here to do a spin
	if (touch.tapCount == 2 && CGRectContainsPoint(player.Rect, touchStart))
	{
		[player Spin];
	}
	
	return YES;
}

// When the player moves their finger across the screen
- (BOOL) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{
	//NSLog(@"Touches moved");	
	return YES;
}

// When the player lifts their finger off the screen
- (BOOL) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{	
	UITouch *touch = [touches anyObject];
	CGPoint point = [touch locationInView: [touch view]];
	touchEnd = [[Director sharedDirector] convertToGL:point];
	
	// Make sure that the start point is from the player
	if (CGRectContainsPoint(player.Rect, touchStart))
	{
		CGFloat deltaX = touchEnd.x - touchStart.x;
		CGFloat deltaY = touchEnd.y - touchStart.y;
		CGFloat distance = sqrt(deltaX * deltaX + deltaY * deltaY);
		// Make sure that the end point is sufficently far from the start point
		if (distance > CONST_KUMQUAT_DRAG_DIST)
		{
			// Flip to the correect orientation
			// If player is looking to the right and shoots left or if the player is looking the left and shoots right
			if ((player.walk.x > 0 && touchEnd.x < touchStart.x) || (player.walk.x < 0 && touchEnd.x > touchStart.x))
			{
				[player Flip];
			}
			[player Throw];
		}
	}
	
	return YES;
}

///////////////////////////////////////
// Functions called from HUD buttons //
///////////////////////////////////////

// Function called when the right arrow is pressed
- (void) PlayerWalkRight: (id)sender
{
	// Make sure we face the right way
	if (player.walk.x < 0)
	{
		[player Flip];
	}
	[player Walk];
}

// Function called when the left arrow is pressed
- (void) PlayerWalkLeft: (id)sender
{
	// Make sure we face the right way
	if (player.walk.x > 0)
	{
		[player Flip];
	}
	[player Walk];
}

// Function to stop player animations and change state to standing
- (void) PlayerStand: (id)sender
{
	[player Stand];
}

- (void) PlayerSpin: (id)sender
{
	[player Spin];
}

// Dummy function
- (void) PlayerDoNothing: (id)sender
{
}

// UNUSED !!!!! 
- (void) RemoveKumquat:(Kumquat *)kumquat
{
	[kumquat RemoveFromSpace:space];
	[self removeChild:kumquat.sprite cleanup:YES];
	[munitions removeObject:kumquat];
}

- (void) createExplosion:(CGPoint)position
{
	ParticleSystem *emitter = [KumquatExplosion node];
	emitter.position = position;
	[self addChild:emitter];
}

// Happens every half a second
- (void) SlowUpdate
{		
	Munitions *munition;
	// Check if any munitions have expired	
	// Can not use enumerated for-loop since removing object midway through makes it crash
	for (int i = 0; i < munitions.count; i++)
	{
		munition = [munitions objectAtIndex:i];
		// Decrement each object's life timer
		munition.counter--;
		if (munition.counter <= 0)
		{
			[munition RemoveFromSpace:space];
			[self removeChild:munition.sprite cleanup:YES];
			[munitions removeObject:munition];
		}
	}	
}

// Happens every frame
- (void) Update
{
	// Tell chipmunk to take another step in the simulation
	cpSpaceStep(space, 1.0f/60.0f);
	
	// Call our function for each shape
	cpSpaceHashEach(space->activeShapes, &UpdateShape, nil);
	
	// Update the player
	[player Update];
	
	// Update the enemies
	for (Enemy *enemy in enemies)
	{
		[enemy update];
	}
}

void UpdateShape(void * ptr, void * unused)
{
	// Get our shape
	cpShape *shape = (cpShape *)ptr;
	
	AtlasSprite *sprite = (AtlasSprite *)((GameObject *)shape->data).sprite;
	
	// Update the sprite's position and rotation based on the physics model
	[sprite setPosition:CGPointMake(shape->body->p.x, shape->body->p.y)];
	[sprite setRotation:CC_RADIANS_TO_DEGREES(shape->body->a)];	
}

- (void) dealloc
{
	[player release];
	[meerkat release];
	[munitions release];
	[super dealloc];
}

@end
